import { SendMineMobileVcodeDto, SendMobileVcodeDto } from '@/module/vcode/vcode.dto';
import { Injectable } from '@nestjs/common';
import { RedisService } from "@liaoliaots/nestjs-redis";
import { Redis } from 'ioredis';
// import { TencentCaptchaService, } from '@/module/tencent-captcha/tencent-captcha.service';
import { InjectRepository } from "@nestjs/typeorm";
import { Repository } from "typeorm";
import { UserModel } from "@/module/user/user.model";
import { customAlphabet, nanoid } from 'nanoid';
import { RedisNamespace } from "@/common/config/config.interface";
import { BizFailedException } from "@/common/biz-failed/biz-failed.exception";
import { BizFailedCodeEnum } from "@/common/biz-failed/biz-failed.enum";
// import { TencentCaptchaVerifyType } from "@/module/tencent-captcha/tencent-captcha.enum";
import { SendMineVcodeType, SendVcodeType } from "common/utils/data";
import { ConfigService } from "@nestjs/config";
import Dysmsapi20170525Client, { QuerySendDetailsRequest, SendSmsRequest } from '@alicloud/dysmsapi20170525';
import { Config as AliCloudOpenApiClientConfig } from '@alicloud/openapi-client';
import { LoggerService } from "@/common/logger/logger.service";
import dayjs from 'dayjs';

@Injectable()
export class VcodeService {
  private readonly vcodeRedisClient: Redis;
  private readonly vcodeExpireMinutes = 12 * 60; // 验证码过期时间 12小时
  private readonly vcodeGener = customAlphabet('0123456789', 6);
  private readonly aliyunSmsClient: Dysmsapi20170525Client
  private readonly aliyunSmsSignName = '青岛恒德瑞电子科技';

  constructor(
    private readonly redisService: RedisService,
    private readonly loggerService: LoggerService,
    private readonly configService: ConfigService,
    // private readonly tencentCaptchaService: TencentCaptchaService,
    @InjectRepository(UserModel)
    private readonly userModelRepository: Repository<UserModel>,
  ) {
    this.vcodeRedisClient = this.redisService.getClient(RedisNamespace.v_code);

    const aliYunConfig = new AliCloudOpenApiClientConfig({
      accessKeyId: this.configService.get<string>('ALIYUN_ACCESS_KEY_ID'),
      accessKeySecret: this.configService.get<string>('ALIYUN_ACCESS_KEY_SECRET'),
    });
    aliYunConfig.endpoint = "dysmsapi.aliyuncs.com";
    this.aliyunSmsClient = new Dysmsapi20170525Client(aliYunConfig);
  }

  /*async sendEmailVcode(body: SendEmailVcodeDto): Promise<string> {
   /!* await this.tencentCaptchaService.verify({
      type: TencentCaptchaVerifyType.EMAIL_MOBILE,
      Ticket: body.ticket,
      Randstr: body.randstr,
      UserIP: ip,
    });*!/
    if(body.vcodeKey){ // 判断是否在60秒之内同一个验证码按钮又发起了
      const exMinutes = await this.vcodeRedisClient.ttl(body.vcodeKey);
      if(this.vcodeExpireMinutes * 60 - exMinutes < 60){
        throw new BizFailedException(BizFailedCodeEnum.VCODE_SEND_FREQUENCY)
      }
    }
    // 校验邮箱一波, 如果是重置密码，必须存在。如果是注册，必须不存在
    const count = await this.userModelRepository.count({
      where: {
        mobile:body.email
      }
    });
    switch (body.type) {
      case SendVcodeType.USER_REGISTER:
        if(count){
          throw new BizFailedException(BizFailedCodeEnum.USER_NAME_EXISTS)
        }
        break;
      case SendVcodeType.USER_LOGIN:
      case SendVcodeType.USER_RESET_PWD:
        if(!count){
          throw new BizFailedException(BizFailedCodeEnum.USER_EMAIL_NOT_EXISTS)
        }
        break;
    }
    const vcode = this.vcodeGener();
    const vcodeKey = nanoid();
    await this.vcodeRedisClient.set(vcodeKey, vcode, 'EX', this.vcodeExpireMinutes * 60);
    switch (body.type) {
      case SendVcodeType.USER_REGISTER:
        await this.mailerService.sendCreateUserVcode({
          vcode,
          to: body.email,
          expire: `${this.vcodeExpireMinutes}分钟`,
        });
        break;
      case SendVcodeType.USER_LOGIN:
        await this.mailerService.sendLoginVcode({
          vcode,
          to: body.email,
          expire: `${this.vcodeExpireMinutes}分钟`,
        });
        break;
      case SendVcodeType.USER_RESET_PWD:
        await this.mailerService.sendResetPwdVcode({
          vcode,
          to: body.email,
          expire: `${this.vcodeExpireMinutes}分钟`,
        });
        break;
    }
    return vcodeKey;
  }*/

  async sendMobileVcode(body: SendMobileVcodeDto): Promise<string> {
    /* await this.tencentCaptchaService.verify({
       type: TencentCaptchaVerifyType.EMAIL_MOBILE,
       Ticket: body.ticket,
       Randstr: body.randstr,
       UserIP: ip,
     });*/
    if (body.vcodeKey) { // 判断该key是否还能用
      const res = await this.vcodeRedisClient.get(body.vcodeKey);
      if (res) {
        return body.vcodeKey;
      }
    }
    // 校验手机一波, 如果是重置密码，必须存在。如果是注册，必须不存在
    const count = await this.userModelRepository.count({
      where: {
        mobile: body.mobile
      }
    });
    switch (body.type) {
      case SendVcodeType.USER_REGISTER:
        if (count) {
          throw new BizFailedException(BizFailedCodeEnum.USER_MOBILE_EXISTS)
        }
        break;
      case SendVcodeType.USER_LOGIN:
      case SendVcodeType.USER_RESET_PWD:
        if (!count) {
          throw new BizFailedException(BizFailedCodeEnum.USER_MOBILE_NOT_EXISTS)
        }
        break;
    }
    const vcode = this.vcodeGener();
    const vcodeKey = nanoid();
    switch (body.type) {
      case SendVcodeType.USER_REGISTER:
        await this.sendAliYunSmsVcode({
          phoneNumbers: body.mobile,
          vcode,
        });
        break;
      case SendVcodeType.USER_LOGIN:
        await this.sendAliYunSmsVcode({
          phoneNumbers: body.mobile,
          vcode,
        });
        break;
      case SendVcodeType.USER_RESET_PWD:
        await this.sendAliYunSmsVcode({
          phoneNumbers: body.mobile,
          vcode,
        });
        break;
    }
    await this.vcodeRedisClient.set(vcodeKey, vcode, 'EX', this.vcodeExpireMinutes * 60);

    return vcodeKey;
  }

  async sendMineMobileVcode(body: SendMineMobileVcodeDto, mobile: string): Promise<string> {
    if (body.vcodeKey) { // 判断该key是否还能用
      const res = await this.vcodeRedisClient.get(body.vcodeKey);
      if (res) {
        return body.vcodeKey;
      }
    }
    const vcode = this.vcodeGener();
    const vcodeKey = nanoid();
    switch (body.type) {
      case SendMineVcodeType.USER_RESET_MY_PWD:
        await this.sendAliYunSmsVcode({
          phoneNumbers: mobile,
          vcode,
        });
        break;
    }

    await this.vcodeRedisClient.set(vcodeKey, vcode, 'EX', this.vcodeExpireMinutes * 60);
    return vcodeKey;
  }

  async verify(
    vcodeKey: string | undefined,
    vcode: string,
    type: SendVcodeType | SendMineVcodeType
  ) {
    if (this.configService.get<string>('VCODE_VERIFY') === '0') {
      return;
    }
    if (!vcodeKey) {
      throw new BizFailedException(BizFailedCodeEnum.VCODE_WRONG);
    }
    const res = await this.vcodeRedisClient.get(vcodeKey);
    if (res !== vcode) {
      throw new BizFailedException(BizFailedCodeEnum.VCODE_WRONG);
    }
    if ([
      SendVcodeType.USER_REGISTER,
      SendVcodeType.USER_RESET_PWD,
      SendMineVcodeType.USER_RESET_MY_PWD
    ].includes(type)) {
      await this.vcodeRedisClient.del(vcodeKey);
    }
  }


  // 底层发短信
  private async sendAliYunSms({
    phoneNumbers,
    templateCode,
    templateParam
  }: {
    phoneNumbers: string,
    templateCode: string;
    templateParam: any;
  }) {
    // const now = dayjs().format('YYYYMMDD');
    const param = {
      phoneNumbers,
      signName: this.aliyunSmsSignName,
      templateCode,
      templateParam: JSON.stringify(templateParam),
    }
    this.loggerService.log('发送短信参数：');
    this.loggerService.log(param);
    const sendSmsRequest = new SendSmsRequest(param);
    const res = await this.aliyunSmsClient.sendSms(sendSmsRequest);
    this.loggerService.log('发送短信结果：');
    this.loggerService.log(res);
    if (res.statusCode !== 200 || res.body.code !== 'OK') {
      throw new BizFailedException(BizFailedCodeEnum.VCODE_SEND_ERROR);
    }
    // await this.checkSmsSendStatus({
    //   bizId: res.body.bizId!,
    //   phoneNumber: phoneNumbers,
    //   sendDate: now,
    // })
  }

  private async checkSmsSendStatus({
    bizId,
    phoneNumber,
    sendDate,
    checkCount = 0,
  }: {
    bizId: string;
    phoneNumber: string;
    sendDate: string;
    checkCount?: number;
  }) {
    await new Promise(resolve => setTimeout(resolve, 3000));
    const param = {
      bizId,
      phoneNumber,
      sendDate,
      pageSize: 1,
      currentPage: 1,
    };
    this.loggerService.log('查询发送详情参数：');
    this.loggerService.log(param);
    const querySendDetailsRequest = new QuerySendDetailsRequest(param)
    const querySendDetailsResponse = await this.aliyunSmsClient.querySendDetails(querySendDetailsRequest);
    this.loggerService.log('查询发送详情结果：');
    this.loggerService.log(querySendDetailsResponse);
    const list = querySendDetailsResponse?.body?.smsSendDetailDTOs?.smsSendDetailDTO;
    if (!list?.length || list?.[0]?.sendStatus === 1) {
      if (checkCount === 10) {
        throw new BizFailedException(BizFailedCodeEnum.VCODE_SEND_ERROR_MAYBE_YIDONG);
      } else {
        await this.checkSmsSendStatus({ bizId, phoneNumber, sendDate, checkCount: checkCount + 1 })
      }
    }
    if (list?.[0]?.sendStatus === 2) {
      throw new BizFailedException(BizFailedCodeEnum.VCODE_SEND_ERROR_MAYBE_YIDONG);
    }
  }

  // 发验证码短信，还有可能发其他短信
  private async sendAliYunSmsVcode({
    phoneNumbers,
    vcode,
  }: {
    phoneNumbers: string,
    vcode: string;
  }) {
    await this.sendAliYunSms({
      phoneNumbers,
      templateCode: 'SMS_485360156',
      templateParam: {
        code: vcode,
      }
    })
  }
}
